#ifndef XG_OPENSSL_SSLSOCKET_H
#define XG_OPENSSL_SSLSOCKET_H
/////////////////////////////////////////////////////////////////
#include <openssl/ssl.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rand.h>  
#include <openssl/crypto.h>
#include "../stdx/Socket.h"

typedef SSL* SSL_SOCKET;
typedef SSL_CTX* SSL_CONTEXT;

#ifndef OPENSSL_VERSION_1_0_0
#define OPENSSL_VERSION_1_0_0	0x10000000L
#endif

#ifndef OPENSSL_VERSION_1_0_2
#define OPENSSL_VERSION_1_0_2	0x10002000L
#endif

class SSLContext : public Object
{
protected:
	SSL_CONTEXT ctx = NULL;

public:
	~SSLContext();
	void destroy();
	bool setMode(long mode);
	bool init(int version = 0);
	SSL_CONTEXT getHandle() const;
	bool setClientVerify(bool checked);
	bool setCipherList(const string& str = "RC4-MD5");
	bool setClientCertificate(const string& certfile);
	bool setCertificate(const string& certfile, int type = SSL_FILETYPE_PEM);
	bool setCertPrivateKey(const string& keyfile, int type = SSL_FILETYPE_PEM);

	static SSLContext* GetGlobalContext();
	static int SelectALPN(SSL* ssl, const u_char** out, u_char* outlen, const u_char* in, u_int inlen, void* arg);
};

class SSLSocket : public Socket
{
protected:
	SSL_SOCKET ssl = NULL;
	SSLContext* ctx = NULL;

public:
	~SSLSocket();
	void close();
	int accept();
	SSL_SOCKET getHandle() const;
	bool init(SOCKET sock = INVALID_SOCKET);
	bool init(SOCKET sock, SSLContext* ctx);
	bool connect(const string& ip, int port, int timeout = SOCKET_CONNECT_TIMEOUT);

	int peek(void* data, int size);
	int read(void* data, int size);
	int write(const void* data, int size);
	int read(void* data, int size, bool completed);
	int tryCheck(int timeout = 0, bool ckrd = true);
	int write(const void* data, int size, bool completed);
};

class SSLSocketPool : public SocketPool
{
public:
	SSLSocketPool(const string& host, int port);
	static sp<Socket> Connect(const string& host, int port);
};
/////////////////////////////////////////////////////////////////
#endif